#pragma once

#include "rewine/IExport.hpp"

#include "rewine/ValueSynchronizer.hpp"

#include "wininc/primitive.h"
#include "wininc/wstr.h"

#include <functional>
#include <memory>
#include <string>

namespace rewine
{

class CExport : public IExport {
private:

    // To ensure CExport is released after no one is using m_Synchronizer
    bool m_Reffed = true;

    unsigned int        m_Index = -1;
    std::string         m_Name;
    sync_value_t        m_Value;

    ValueSynchronizer   m_Synchronizer;

private:

bool setValueHandler(sync_value_t value) {
    if (getValueHandler(false) == value) {
        return false;
    }
    m_Value = value;
    return true;
}

    sync_value_t getValueHandler(bool bResolve) {
        return m_Value;
    }

protected:

    CExport()
            : IExport(), m_Reffed(true), m_Value() {
        m_Synchronizer.onReleaseHandler = std::bind(&CExport::tryToSuicide, this, false);
        m_Synchronizer.setValueHandler = std::bind(&CExport::setValueHandler, this, std::placeholders::_1);
        m_Synchronizer.getValueHandler = std::bind(&CExport::getValueHandler, this, true);
    }

    ~CExport() override = default;

public:

    /*
    static std::shared_ptr<CExport> create() {
        auto&& p = std::shared_ptr<CExport>(new CExport(), getDeleter());
        return std::move(p);
    }
    */

private:

    void tryToSuicide(bool bDeref) {
        if (bDeref) {
            m_Reffed = false;
        }
        if (!m_Reffed && m_Synchronizer.GetRefCount() == 0) {
            suicide();
        }
    }

protected:

    virtual void suicide() {
        delete this;
    }

    static std::function<void(CExport *)> getDeleter() {
        return std::bind(&CExport::tryToSuicide, std::placeholders::_1, true);
    }

public:

    void setIndex(unsigned int index) {
        m_Index = index;
    }

    void setName(const std::string &name) {
        m_Name = name;
    }

    unsigned int getIndex() const override {
        return m_Index;
    }

    std::string getName() const override {
        return m_Name;
    }

public:

    void setInvalid() override {
        setSyncValue(sync_value_t());
    }

    bool isValid() override {
        return getSyncValue().valid;
    }

    void setValue(void *p) override {
        setSyncValue(sync_value_t(true, p));
    }

    void *getValue() override {
        return getSyncValue().address;
    }

public:

    void setSyncValue(sync_value_t value) {
        m_Synchronizer.setValue(value, true);
    }

    sync_value_t getSyncValue() {
        return m_Synchronizer.getValue();
    }

    ValueSynchronizer& getValueSynchronizerRef() override {
        return m_Synchronizer;
    }

    void bindTo(ValueSynchronizer& source) override {
        m_Synchronizer.follow(source);
    }

    void unbind() override {
        m_Synchronizer.unfollow();
    }

};

} // namespace rewine
